package com.partsoft.webs.zks;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.ForwardEvent;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;

import com.partsoft.utils.StringUtils;
import com.partsoft.webs.log.Log;

/**
 * 
<p>每个对应的Listbox的数据宏组件必须如下编写：</p>
<p>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;<br />
  &lt;!-- <br />
  组件名称: 描述组件名称<br />
  应用标签: 组件标签<br />
  --&gt;<br />
  &lt;zk&gt;<br />
  &lt;borderlayout&gt;<br />
  &lt;south border=&quot;none&quot; visible=&quot;@{dataModel.paging}&quot;&gt;<br />
  &lt;paging id=&quot;dataPaging&quot; totalSize=&quot;@{dataModel.totalSize}&quot; <br />
  pageSize=&quot;@{dataModel.pageSize}&quot;<br />
  activePage=&quot;@{dataModel.activePage, access='both', save-after='dataPaging.onPaging'}&quot;<br />
  detailed=&quot;true&quot; visible=&quot;@{dataModel.paging}&quot; mold=&quot;os&quot;/&gt;<br />
  &lt;/south&gt;<br />
  &lt;center flex=&quot;true&quot; border=&quot;none&quot;&gt;<br />
  &lt;listbox fixedLayout=&quot;true&quot;<br />
  model=&quot;@{dataModel.items, load-after='dataPaging.onPaging'}&quot;<br />
  multiple=&quot;${arg.multiple}&quot; checkmark=&quot;${arg.checkmark}&quot;<br />
  id=&quot;dataListbox&quot;&gt;<br />
  &lt;listhead sizable=&quot;true&quot;&gt;<br />
  &lt;listheader label=&quot;类型&quot; align=&quot;center&quot; width=&quot;52px&quot; sort=&quot;none&quot; onSort=&quot;Events.postEvent(Events.ON_SORT, self.getListbox(), self)&quot;&gt;<br />
  &lt;custom-attributes columnName=&quot;type&quot; /&gt;<br />
  &lt;/listheader&gt;<br />
  ...</p>
<p>&lt;listheader/&gt;<br />
  &lt;/listhead&gt;<br />
  &lt;listitem self=&quot;@{each=item, value=item}&quot; onDoubleClick=&quot;Events.postEvent(new Event(Events.ON_DOUBLE_CLICK, (Component)spaceOwner, self.getValue()))&quot;&gt;<br />
  &lt;listcell label=&quot;@{item....}&quot;/&gt;</p>
<p>...<br />
  &lt;listcell/&gt;<br />
  &lt;/listitem&gt;<br />
  &lt;/listbox&gt;<br />
  &lt;/center&gt;<br />
  &lt;/borderlayout&gt;<br />
  &lt;/zk&gt;</p>
<p>然后在/metainfo/zk/lang-addon.xml文件中如下定义：</p>
<p> &lt;component&gt;<br />
&lt;component-name&gt;组件标签&lt;/component-name&gt;<br />
&lt;component-class&gt;com.partsoft.webs.zks.SmartListbox&lt;/component-class&gt;<br />
&lt;inline&gt;false&lt;/inline&gt;<br />
&lt;macro-uri&gt;你的组件zul页面地址(以~./开头表示类路径/web/开始)&lt;/macro-uri&gt;</p>
<p>&lt;!--其他自定义属性--&gt;<br />
  &lt;property&gt;<br />
  &lt;property-name&gt;checkmark&lt;/property-name&gt;<br />
  &lt;property-value&gt;false&lt;/property-value&gt;<br />
  &lt;/property&gt;<br />
  &lt;property&gt;<br />
  &lt;property-name&gt;multiple&lt;/property-name&gt;<br />
  &lt;property-value&gt;false&lt;/property-value&gt;<br />
  &lt;/property&gt;<br />
  &lt;/component&gt;</p>
 */
@SuppressWarnings("unchecked")
public class SmartListbox<E> extends AnnotateDataBinderComponent implements Cursor<E> {

	private static final long serialVersionUID = 4808501777670753646L;

	protected static final String LISTBOX_ID = "dataListbox";
	
	protected DelegateListModel logListModel = new DelegateListModel();
	
	protected E currentSelectObject;
	
	private String sortColumn;
	
	private String  sortDirection = "none";
	
	private EventListener<Event> dataSortEventListener = new EventListener<Event>() {
		public void onEvent(Event event) throws Exception {
			doSortEvent(event);
		}
	};
	
	public SmartListbox() {
		super();
		setDataModel(logListModel);
	}
	
	public SmartListbox(String macroURI) {
		super(macroURI);
		setDataModel(new DelegateListModel());
	}
	
	@Override
	protected void afterCreateMacroChildren() {
		super.afterCreateMacroChildren();
		Listbox result = (Listbox) this.getFellowIfAny(LISTBOX_ID);
		if (result != null) {
			result.addEventListener(Events.ON_SORT, dataSortEventListener);
		}
	}
	
	@Override
	public void setDataModel(Object dataModel) {
		if (dataModel != null) {
			Assert.isInstanceOf(ListModel.class, dataModel, String.format("必须设置为类(%s)的对象", ListModel.class.getName()));
			if (!ObjectUtils.nullSafeEquals(dataModel, this.logListModel)) {
				this.logListModel.setDelegateModel((ListModel<Object>) dataModel);
			}
		} else {
			this.logListModel.setDelegateModel(null);
		}
		super.setDataModel(this.logListModel);
	}
	
	public void setDataReload(boolean reload) {
		if (reload) loadData();
	}
	
	@Override
	public ListModel<E> getDataModel() {
		return (ListModel<E>) super.getDataModel();
	}
	
	protected Listbox getListbox() {
		Listbox result = null;
		if (isAlreadyCompose()) {
			result = (Listbox) this.getFellowIfAny(LISTBOX_ID);
		}
		return result;
	}
	
	protected void doSortEvent(Event sEvent) {
		Event oEvent = sEvent instanceof ForwardEvent ? ((ForwardEvent)sEvent).getOrigin() : sEvent;
		if (oEvent.getData() instanceof Listheader) {
			if (((Listheader)oEvent.getData()).hasAttribute("columnName")) {
				sortColumn =  ((Listheader)oEvent.getData()).getAttribute("columnName").toString();
				sortDirection = ((Listheader)oEvent.getData()).getSortDirection();
				Events.postEvent(new Event(Events.ON_SORT, (Component)getSpaceOwner(), new String[]{sortColumn, sortDirection}));
			}
		} else {
			Log.warn("排序事件的数据对象为Listheader实例。");
		}
	}
	
	public boolean isSorting() {
		return StringUtils.hasText(this.sortColumn) && !ObjectUtils.nullSafeEquals(sortDirection, "none");
	}
	
	public String getSortDirection() {
		return sortDirection;
	}
	
	public String getSortColumn() {
		return sortColumn;
	}

	public boolean hasPrevious() {
		boolean result = false;
		if (isAlreadyCompose()) {
			Listbox listbox = getListbox();
			if (listbox != null) {
				if (listbox.getSelectedIndex() > 0 || getDataModel().getActivePage() > 0) {
					result = true;
				}
			}
		}
		return result;
	}
	
	public E previous() {
		Object result = null;
		if (isAlreadyCompose()) {
			Listbox listbox = getListbox();
			if (listbox != null) {
				if (listbox.getSelectedIndex() > 0) {
					listbox.setSelectedIndex(listbox.getSelectedIndex() - 1);
					result = getDataModel().getItems().get(listbox.getSelectedIndex());
					currentSelectObject = (E) result;
				} else if (getDataModel().getActivePage() > 0) {
					getDataModel().setActivePage(getDataModel().getActivePage() - 1);
					super.loadData();
					if (listbox.getItemCount() > 0) {
						listbox.setSelectedIndex(getDataModel().getPageSize() - 1);
						result = getDataModel().getItems().get(listbox.getSelectedIndex());
						currentSelectObject = (E) result;
					}
				}
			}
		}
		return (E) result;
	}

	public boolean hasNext() {
		boolean result = false;
		if (isAlreadyCompose()) {
			Listbox listbox = getListbox();
			if (listbox != null) {
				int maxPage = getDataModel().getTotalSize() / getDataModel().getPageSize() + ((getDataModel().getTotalSize() % getDataModel().getPageSize()) > 0 ? 1 : 0);
				if (getDataModel().getActivePage() < (maxPage - 1)) {
					result = true;
				} else if (listbox.getSelectedIndex() < (listbox.getItemCount() - 1)) {
					result = true;
				}
			}
		}
		return result;
	}
	

	public E next() {
		Object result = null;
		if (isAlreadyCompose()) {
			Listbox listbox = getListbox();
			if (listbox != null) {
				int maxPage = getDataModel().getTotalSize() / getDataModel().getPageSize() + ((getDataModel().getTotalSize() % getDataModel().getPageSize()) > 0 ? 1 : 0);
				if (listbox.getSelectedIndex() < (listbox.getItemCount() - 1)) {
					listbox.setSelectedIndex(listbox.getSelectedIndex() + 1);
					result = getDataModel().getItems().get(listbox.getSelectedIndex());
					currentSelectObject = (E) result;
				} else if (getDataModel().getActivePage() < (maxPage - 1)) {
					getDataModel().setActivePage(getDataModel().getActivePage() + 1);
					super.loadData();
					if (listbox.getItemCount() > 0) {
						listbox.setSelectedIndex(0);
						result = getDataModel().getItems().get(0);
						currentSelectObject = (E) result;
					}
				}
			}
		}
		return (E)result;
	}

	public E get() {
		if (currentSelectObject == null) {
			if (isAlreadyCompose()) {
				Listbox listbox = getListbox();
				if (listbox != null) {
					if (listbox.getSelectedIndex() < 0 && getDataModel().getItems().size() > 0) {
						listbox.setSelectedIndex(0);
					} else if (listbox.getSelectedIndex() > 0 && getDataModel().getItems().size() > 0 &&
							listbox.getSelectedIndex() >= getDataModel().getItems().size() ) {
						listbox.setSelectedIndex(getDataModel().getItems().size() - 1);
					}
					if (listbox.getSelectedIndex() >= 0) {
						currentSelectObject = getDataModel().getItems().get(listbox.getSelectedIndex());
					}
				}
			}		
		}
		return currentSelectObject;
	}
	
	public void refresh() {
		getDataModel().refresh();
		loadData();
	}
	
	@Override
	public void loadData() {
		super.loadData();
		if (isAlreadyCompose()) {
			if (getListbox().getSelectedCount() == 0) {
				currentSelectObject = null;
			}
		}
	}
	
	public void loadSelected() {
		if (isAlreadyCompose()) {
			Listbox listbox = getListbox();
			if (listbox != null && getListbox().getSelectedCount() != 0) {
				for (Listitem item : getListbox().getSelectedItems()) {
					getDataBinder().loadComponent(item);
				}
			}
		}
	}

	@Override
	protected void loadDataModelParameter(Object dataModel) {}

	public class DelegateListModel implements ListModel<Object>, Serializable {
		
		private static final long serialVersionUID = -9179025840753500411L;
		
		private transient ListModel<Object> delegate;
		
		public void setDelegateModel(ListModel<Object> delegate) {
			this.delegate = delegate;
		}

		public List<Object> getItems() {
			return delegate == null ? Collections.EMPTY_LIST : delegate.getItems();
		}
		
		public int getActivePage() {
			return delegate == null ? 0 : delegate.getActivePage();
		}

		public void setActivePage(int activePage) {
			if (delegate != null) {
				if (activePage != delegate.getActivePage()) {
					delegate.setActivePage(activePage);
					if (currentSelectObject != null) {
						getListbox().setSelectedIndex(-1);
						currentSelectObject = null;
					}
					currentSelectObject = get();
					Listbox listbox = getListbox();
					if (listbox != null) {
						Events.postEvent(new Event(Events.ON_SELECT, (Component)getSpaceOwner(), listbox.getSelectedItems()));
					}
				}
			}
		}

		public int getPageSize() {
			return delegate == null ? 30 : delegate.getPageSize();
		}

		public int getTotalSize() {
			return delegate == null ? 0 : delegate.getTotalSize();
		}

		public boolean isPaging() {
			return delegate == null ? true : delegate.isPaging();
		}
		
		public void refresh() {
			if (delegate != null) delegate.refresh();
		}
		
		public void setSelectObject(E obj) {
			currentSelectObject = obj;
		}
		
		public Object getSelectObject() {
			return currentSelectObject;
		}

	}
	
}
